package com.aiwu;

import android.content.Context;
import android.content.DialogInterface;
import android.net.DhcpInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.EditorInfo;
import android.widget.ImageView;

import com.aiwu.library.App;
import com.aiwu.library.abs.ui.AbsQuickSettingBottomActionDialog;
import com.aiwu.library.abs.ui.AbsQuickSettingDialog;
import com.aiwu.library.bean.setting.EditTextSettingBean;
import com.aiwu.library.bean.setting.TextSettingBean;
import com.aiwu.library.ui.adapter.SettingMultipleTypeAdapter;
import com.aiwu.library.util.ClipboardUtil;
import com.aiwu.library.util.DialogUtil;
import com.aiwu.library.util.LogUtil;
import com.aiwu.library.util.ThreadPoolManager;
import com.aiwu.library.util.ToastUtil;
import com.aiwu.utils.PrefsHelper;
import com.chad.library.adapter.base.entity.MultiItemEntity;

import org.citra.citra_emu.CitraApplication;
import org.citra.citra_emu.NativeLibrary;
import org.citra.citra_emu.R;
import org.citra.citra_emu.activities.EmulationActivity;
import org.citra.citra_emu.fragments.EmulationFragment;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * 局域网对战
 */
public class NetPlayManager {
    /**
     * Regex of ip address.
     */
    private static final String REGEX_IP = "((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)";
    private static final String REGEX_NAME_INPUT = "[0-9a-zA-Z]*";
    private static final String REGEX_IP_INPUT = "[0-9\\.]*";
    private volatile static NetPlayManager instance;

    private AbsQuickSettingBottomActionDialog<?> connectToRoomDialog;
    private DialogInterface netPlayMainDialog;
    private AbsQuickSettingBottomActionDialog<?> netPlayRoomInfoDialog;
    private NetPlayListener netPlayListener;

    public static NetPlayManager getInstance() {
        if (instance == null) {
            synchronized (NetPlayManager.class) {
                if (instance == null) {
                    instance = new NetPlayManager();
                }
            }
        }
        return instance;
    }

    private NetPlayManager() {
    }

    public void onNetPlayStatusMessageReceive(int type, String msg) {
        CitraApplication.getMainHandler().post(() -> {
            if (isDismissDialogStatus(type)) {
                if (connectToRoomDialog != null) {
                    connectToRoomDialog.dismiss();
                }
                if (netPlayMainDialog != null) {
                    netPlayMainDialog.dismiss();
                }
            }
            if (netPlayRoomInfoDialog != null) {
                if (type == NetPlayStatus.LOST_CONNECTION || type == NetPlayStatus.HOST_KICKED || type == NetPlayStatus.ROOM_UNINITIALIZED || type == NetPlayStatus.ROOM_IDLE || type == NetPlayStatus.ROOM_MODERATOR || type == NetPlayStatus.ADDRESS_UNBANNED) {
                    netPlayRoomInfoDialog.dismiss();
                } else if (type == NetPlayStatus.ROOM_JOINED || type == NetPlayStatus.MEMBER_LEAVE || type == NetPlayStatus.MEMBER_JOIN || type == NetPlayStatus.MEMBER_KICKED || type == NetPlayStatus.MEMBER_BANNED) {
                    if (netPlayRoomInfoDialog.isShowing()) {
                        String[] infos = NativeLibrary.netPlayRoomInfo25();
                        netPlayRoomInfoDialog.setNewData(getRoomInfoSettingData(infos));
                    }
                }
            }
            String formatMsg = FormatNetPlayStatus(type, msg);
            LogUtil.e(type + "," + msg + "," + formatMsg);
            addNetPlayMessage(formatMsg);
        });
    }

    public void addNetPlayMessage(String formatMsg) {
        if (!TextUtils.isEmpty(formatMsg)) {
            if (netPlayListener != null) {
                netPlayListener.onAddNetPlayMessage(formatMsg);
            }
        }
        EmulationActivity emulationActivity = NativeLibrary.sEmulationActivity.get();
        if (emulationActivity != null) {
            EmulationFragment emulationFragment = emulationActivity.getEmulationFragment();
            if (emulationFragment != null) {
                emulationFragment.addNetPlayMessage(formatMsg);
                return;
            }
        }
        ToastUtil.toast(formatMsg);
    }

    private static boolean isDismissDialogStatus(int type) {
        return type == NetPlayStatus.LOST_CONNECTION || type == NetPlayStatus.HOST_KICKED || type == NetPlayStatus.ROOM_UNINITIALIZED || type == NetPlayStatus.ROOM_IDLE || type == NetPlayStatus.ROOM_JOINED || type == NetPlayStatus.ROOM_MODERATOR || type == NetPlayStatus.MEMBER_JOIN;
    }

    private static String FormatNetPlayStatus(int type, String msg) {
        return switch (type) {
            case NetPlayStatus.NETWORK_ERROR ->
                    App.getContext().getString(R.string.multiplayer_network_error);
            case NetPlayStatus.LOST_CONNECTION ->
                    App.getContext().getString(R.string.multiplayer_lost_connection);
            case NetPlayStatus.NAME_COLLISION ->
                    App.getContext().getString(R.string.multiplayer_name_collision);
            case NetPlayStatus.MAC_COLLISION ->
                    App.getContext().getString(R.string.multiplayer_mac_collision);
            case NetPlayStatus.CONSOLE_ID_COLLISION ->
                    App.getContext().getString(R.string.multiplayer_console_id_collision);
            case NetPlayStatus.WRONG_VERSION ->
                    App.getContext().getString(R.string.multiplayer_wrong_version);
            case NetPlayStatus.WRONG_PASSWORD ->
                    App.getContext().getString(R.string.multiplayer_wrong_password);
            case NetPlayStatus.COULD_NOT_CONNECT ->
                    App.getContext().getString(R.string.multiplayer_could_not_connect);
            case NetPlayStatus.ROOM_IS_FULL ->
                    App.getContext().getString(R.string.multiplayer_room_is_full);
            case NetPlayStatus.HOST_BANNED ->
                    App.getContext().getString(R.string.multiplayer_host_banned);
            case NetPlayStatus.PERMISSION_DENIED ->
                    App.getContext().getString(R.string.multiplayer_permission_denied);
            case NetPlayStatus.NO_SUCH_USER ->
                    App.getContext().getString(R.string.multiplayer_no_such_user);
            case NetPlayStatus.ALREADY_IN_ROOM ->
                    App.getContext().getString(R.string.multiplayer_already_in_room);
            case NetPlayStatus.CREATE_ROOM_ERROR ->
                    App.getContext().getString(R.string.multiplayer_create_room_error);
            case NetPlayStatus.HOST_KICKED ->
                    App.getContext().getString(R.string.multiplayer_host_kicked);
            case NetPlayStatus.UNKNOWN_ERROR ->
                    App.getContext().getString(R.string.multiplayer_unknown_error);
            case NetPlayStatus.ROOM_UNINITIALIZED ->
                    App.getContext().getString(R.string.multiplayer_room_uninitialized);
            case NetPlayStatus.ROOM_IDLE ->
                    App.getContext().getString(R.string.multiplayer_room_idle);
            case NetPlayStatus.ROOM_JOINING ->
                    App.getContext().getString(R.string.multiplayer_room_joining);
            case NetPlayStatus.ROOM_JOINED ->
                    App.getContext().getString(R.string.multiplayer_room_joined);
            case NetPlayStatus.ROOM_MODERATOR ->
                    App.getContext().getString(R.string.multiplayer_room_moderator);
            case NetPlayStatus.MEMBER_JOIN ->
                    App.getContext().getString(R.string.multiplayer_member_join, msg);
            case NetPlayStatus.MEMBER_LEAVE ->
                    App.getContext().getString(R.string.multiplayer_member_leave, msg);
            case NetPlayStatus.MEMBER_KICKED ->
                    App.getContext().getString(R.string.multiplayer_member_kicked, msg);
            case NetPlayStatus.MEMBER_BANNED ->
                    App.getContext().getString(R.string.multiplayer_member_banned, msg);
            case NetPlayStatus.ADDRESS_UNBANNED ->
                    App.getContext().getString(R.string.multiplayer_address_unbanned);
            case NetPlayStatus.CHAT_MESSAGE -> msg;
            default -> "";
        };
    }

    /**
     * 切换连接到房间的加载状态
     *
     * @param show
     */
    private void switchConnectedRoomLoading(boolean show) {
        CitraApplication.getMainHandler().post(() -> {
            if (connectToRoomDialog == null || !connectToRoomDialog.isShowing()) {
                return;
            }
            final View loadingView = connectToRoomDialog.findViewById(R.id.layout_loading);
            final ImageView ivLoading = connectToRoomDialog.findViewById(R.id.iv_loading);
            if (show) {
                connectToRoomDialog.setCancelable(false);
                loadingView.setVisibility(View.VISIBLE);
                ivLoading.startAnimation(AnimationUtils.loadAnimation(connectToRoomDialog.getContext(), R.anim.emu_lib_loading_anim));
            } else {
                connectToRoomDialog.setCancelable(true);
                loadingView.setVisibility(View.GONE);
                ivLoading.clearAnimation();
            }
        });
    }

    /**
     * 弹窗：局域网对战
     *
     * @param context
     */
    public static void showNetPlayDialog(final Context context) {
        String[] infos = NativeLibrary.netPlayRoomInfo25();
        if (infos.length > 0) {
            //已经在房间里
            showNetPlayRoomInfoDialog(context, infos);
        } else {
            //没在房间里
            showNetPlayMainDialog(context);
        }
    }

    private static List<MultiItemEntity> getRoomInfoSettingData(String[] infos) {
        List<MultiItemEntity> list = new ArrayList<>();
        list.add(new TextSettingBean(App.getContext().getString(R.string.fight_ip), infos[0]));
        list.add(new TextSettingBean(App.getContext().getString(R.string.fight_port), infos[1]));
        if (infos.length > 2) {
            for (int i = 2; i < infos.length; i++) {
                list.add(new TextSettingBean(App.getContext().getString(R.string.fight_player, i - 1), infos[i]));
            }
        }
        return list;
    }

    /**
     * 弹窗：对战房间
     */
    private static void showNetPlayRoomInfoDialog(final Context context, String[] infos) {
        new AbsQuickSettingBottomActionDialog<>(context, true, false) {
            @Override
            protected void initView() {
                super.initView();
                tvTitle.setText(R.string.fight_room_info);
                btnConfirm.setText(R.string.fight_leave_room);
            }

            @Override
            protected void doLogic() {
                super.doLogic();
                settingAdapter.setOnItemClickListener((baseQuickAdapter, view, i) -> {
                    MultiItemEntity item = settingAdapter.getItem(i);
                    if (item instanceof TextSettingBean) {
                        ClipboardUtil.copyText(((TextSettingBean) item).getCurrentText(), true);
                    }
                });
            }

            @Override
            protected SettingMultipleTypeAdapter getSettingAdapter() {
                return new SettingMultipleTypeAdapter();
            }

            @Override
            protected List<MultiItemEntity> getSettingData() {
                return getRoomInfoSettingData(infos);
            }

            @Override
            protected void onActionBtnClick(View view, boolean isConfirmBtn) {
                DialogUtil.showTip(getContext(), NativeLibrary.netPlayIsHostedRoom25() ? R.string.fight_leave_room_host_tip : R.string.fight_leave_room_member_tip, v12 -> {
                    dismiss();
                    NativeLibrary.netPlayLeaveRoom25();
                    ToastUtil.toast(R.string.fight_leave_room_success);
                }, null);
            }

            @Override
            public void onShow(DialogInterface dialog) {
                super.onShow(dialog);
                getInstance().netPlayRoomInfoDialog = this;
            }

            @Override
            public void onDismiss(DialogInterface dialog) {
                super.onDismiss(dialog);
                getInstance().netPlayRoomInfoDialog = null;
            }

            @Override
            protected boolean isDialogPauseSimulator() {
                return false;
            }
        }.show(0.55f, 0.8f, 0.8f, 0.5f);
    }

    /**
     * 弹窗：局域网对战首页
     */
    private static void showNetPlayMainDialog(final Context context) {
        new AbsQuickSettingDialog<>(context) {
            @Override
            protected void initView() {
                super.initView();
                tvTitle.setText(R.string.emu_lib_menu_netplay);
            }

            @Override
            protected void doLogic() {
                super.doLogic();
                settingAdapter.setOnItemClickListener((baseQuickAdapter, view, i) -> {
                    if (i == 0) {
                        MultiItemEntity item = settingAdapter.getItem(i);
                        if (item instanceof TextSettingBean) {
                            ClipboardUtil.copyText(((TextSettingBean) item).getCurrentText(), true);
                        }
                    } else if (i == 1) {
                        showCreateRoomDialog(context);
                    } else if (i == 2) {
                        showConnectToRoomDialog(context);
                    }
                });
            }

            @Override
            protected SettingMultipleTypeAdapter getSettingAdapter() {
                return new SettingMultipleTypeAdapter();
            }

            @Override
            protected List<MultiItemEntity> getSettingData() {
                List<MultiItemEntity> list = new ArrayList<>();
                list.add(new TextSettingBean(context.getString(R.string.fight_device_id), NativeLibrary.netPlayGetConsoleId25()));
                list.add(new TextSettingBean(context.getString(R.string.fight_create_room), true));
                list.add(new TextSettingBean(context.getString(R.string.fight_connect_to_room), true));
                return list;
            }

            @Override
            public void onDismiss(DialogInterface dialog) {
                super.onDismiss(dialog);
                getInstance().netPlayMainDialog = null;
            }

            @Override
            public void onShow(DialogInterface dialog) {
                super.onShow(dialog);
                getInstance().netPlayMainDialog = dialog;
            }

            @Override
            protected boolean isDialogPauseSimulator() {
                return false;
            }
        }.show(0.5f, 0.0f, 0.8f, 0.0f);
    }

    /**
     * 弹窗：创建房间
     */
    private static void showCreateRoomDialog(final Context context) {
        new AbsQuickSettingBottomActionDialog<SettingMultipleTypeAdapter>(context) {
            @Override
            protected void initView() {
                super.initView();
                tvTitle.setText(R.string.fight_create_room);
            }

            @Override
            protected void onActionBtnClick(View view, boolean isConfirmBtn) {
                if (isConfirmBtn) {
                    String ipaddress = ((EditTextSettingBean) settingAdapter.getItem(0)).getCurrentText();
                    String portStr = ((EditTextSettingBean) settingAdapter.getItem(1)).getCurrentText();
                    String username = ((EditTextSettingBean) settingAdapter.getItem(3)).getCurrentText();
                    int port;
                    try {
                        port = Integer.parseInt(portStr);
                    } catch (Exception e) {
                        ToastUtil.toast(R.string.fight_room_port_error);
                        return;
                    }
                    if (TextUtils.isEmpty(ipaddress) || !Pattern.matches(REGEX_IP, ipaddress)) {
                        ToastUtil.toast(R.string.fight_room_ip_error);
                        return;
                    }
                    if (username.length() < 4 || username.length() > 20) {
                        ToastUtil.toast(R.string.fight_username_error);
                        return;
                    }
                    String passwordStr = ((EditTextSettingBean) settingAdapter.getItem(2)).getCurrentText();
                    if (NativeLibrary.netPlayCreateRoom25(ipaddress, port, passwordStr, username) == 0) {
                        SetUsername(username);
                        SetRoomPort(portStr);
                        ToastUtil.toast(R.string.fight_create_room_success);
                        dismiss();
                    } else {
                        ToastUtil.toast(R.string.fight_create_room_error);
                    }
                } else {
                    dismiss();
                }
            }

            @Override
            protected SettingMultipleTypeAdapter getSettingAdapter() {
                return new SettingMultipleTypeAdapter();
            }

            @Override
            protected List<MultiItemEntity> getSettingData() {
                List<MultiItemEntity> list = new ArrayList<>();
                list.add(new EditTextSettingBean(context.getString(R.string.fight_ip), GetIpAddressByWifi(context), context.getString(R.string.fight_input_room_ip), EditorInfo.TYPE_CLASS_TEXT, 30, REGEX_IP_INPUT));
                list.add(new EditTextSettingBean(context.getString(R.string.fight_port), GetRoomPort(), context.getString(R.string.fight_input_room_port), EditorInfo.TYPE_CLASS_NUMBER, 10));
                list.add(new EditTextSettingBean(context.getString(R.string.fight_password), "", context.getString(R.string.fight_input_room_password), EditorInfo.TYPE_CLASS_NUMBER, 10));
                list.add(new EditTextSettingBean(context.getString(R.string.fight_username), GetUsername(), context.getString(R.string.fight_input_username), EditorInfo.TYPE_CLASS_TEXT, 20, REGEX_NAME_INPUT));
                return list;
            }

            @Override
            protected boolean isDialogPauseSimulator() {
                return false;
            }
        }.show(0.6f, 0.9f, 0.85f, 0.0f);
    }

    /**
     * 弹窗：连接到房间
     */
    private static void showConnectToRoomDialog(final Context context) {
        new AbsQuickSettingBottomActionDialog<>(context) {
            @Override
            protected int getLayoutResID() {
                return R.layout.dialog_fight_join_room;
            }

            @Override
            protected void initView() {
                super.initView();
                tvTitle.setText(R.string.fight_connect_to_room);
            }

            @Override
            protected void onActionBtnClick(View view, boolean isConfirmBtn) {
                if (isConfirmBtn) {
                    String ipaddress = ((EditTextSettingBean) settingAdapter.getItem(0)).getCurrentText();
                    String portStr = ((EditTextSettingBean) settingAdapter.getItem(1)).getCurrentText();
                    String username = ((EditTextSettingBean) settingAdapter.getItem(3)).getCurrentText();
                    int port;
                    try {
                        port = Integer.parseInt(portStr);
                    } catch (Exception e) {
                        ToastUtil.toast(R.string.fight_room_port_error);
                        return;
                    }
                    if (TextUtils.isEmpty(ipaddress) || !Pattern.matches(REGEX_IP, ipaddress)) {
                        ToastUtil.toast(R.string.fight_room_ip_error);
                        return;
                    }
                    if (username.length() < 4 || username.length() > 20) {
                        ToastUtil.toast(R.string.fight_username_error);
                        return;
                    }
                    String passwordStr = ((EditTextSettingBean) settingAdapter.getItem(2)).getCurrentText();
                    getInstance().switchConnectedRoomLoading(true);
                    ThreadPoolManager.getInstance().executeTask(() -> {
                        if (NativeLibrary.netPlayJoinRoom25(ipaddress, port, passwordStr, username) == 0) {
                            SetRoomAddress(ipaddress);
                            SetUsername(username);
                            SetRoomPort(portStr);
                            getInstance().switchConnectedRoomLoading(false);
                            dismiss();
                        } else {
                            getInstance().switchConnectedRoomLoading(false);
                        }
                    });
                } else {
                    dismiss();
                }
            }

            @Override
            protected SettingMultipleTypeAdapter getSettingAdapter() {
                return new SettingMultipleTypeAdapter();
            }

            @Override
            protected List<MultiItemEntity> getSettingData() {
                List<MultiItemEntity> list = new ArrayList<>();
                list.add(new EditTextSettingBean(context.getString(R.string.fight_ip), GetRoomAddress(context), context.getString(R.string.fight_input_room_ip), EditorInfo.TYPE_CLASS_TEXT, 30, REGEX_IP_INPUT));
                list.add(new EditTextSettingBean(context.getString(R.string.fight_port), GetRoomPort(), context.getString(R.string.fight_input_room_port), EditorInfo.TYPE_CLASS_NUMBER, 10));
                list.add(new EditTextSettingBean(context.getString(R.string.fight_password), "", context.getString(R.string.fight_input_room_password), EditorInfo.TYPE_CLASS_NUMBER, 10));
                list.add(new EditTextSettingBean(context.getString(R.string.fight_username), GetUsername(), context.getString(R.string.fight_input_username), EditorInfo.TYPE_CLASS_TEXT, 20, REGEX_NAME_INPUT));
                return list;
            }

            @Override
            public void onShow(DialogInterface dialog) {
                super.onShow(dialog);
                getInstance().connectToRoomDialog = this;
            }

            @Override
            public void onDismiss(DialogInterface dialog) {
                super.onDismiss(dialog);
                getInstance().connectToRoomDialog = null;
            }

            @Override
            protected boolean isDialogPauseSimulator() {
                return false;
            }
        }.show(0.6f, 0.9f, 0.85f, 0.0f);
    }

    public NetPlayListener getNetPlayListener() {
        return netPlayListener;
    }

    public void setNetPlayListener(NetPlayListener netPlayListener) {
        this.netPlayListener = netPlayListener;
    }

    private static String GetIpAddressByWifi(final Context context) {
        int ipaddress = 0;
        WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        WifiInfo wifiInfo = wifiManager.getConnectionInfo();
        if (wifiInfo != null) {
            ipaddress = wifiInfo.getIpAddress();
        }

        if (ipaddress == 0) {
            DhcpInfo dhcpInfo = wifiManager.getDhcpInfo();
            if (dhcpInfo != null) {
                ipaddress = dhcpInfo.ipAddress;
            }
        }

        if (ipaddress == 0) {
            return "192.168.0.1";
        } else {
            return Formatter.formatIpAddress(ipaddress);
        }
    }

    public static String GetUsername() {
        String name = "25game" + (int) (Math.random() * 100);
        return PrefsHelper.getInstance().getNetPlayUsername(name);
    }

    private static void SetUsername(final String name) {
        PrefsHelper.getInstance().setNetPlayUsername(name);
    }

    private static String GetRoomAddress(Context context) {
        String address = GetIpAddressByWifi(context);
        return PrefsHelper.getInstance().getRoomAddress(address);
    }

    private static void SetRoomAddress(final String address) {
        PrefsHelper.getInstance().setRoomAddress(address);
    }

    private static String GetRoomPort() {
        return PrefsHelper.getInstance().getRoomPort("24872");
    }

    private static void SetRoomPort(final String port) {
        PrefsHelper.getInstance().setRoomPort(port);
    }

    public static final class NetPlayStatus {
        public static final int NO_ERROR = 0;

        public static final int NETWORK_ERROR = 1;
        public static final int LOST_CONNECTION = 2;
        public static final int NAME_COLLISION = 3;
        public static final int MAC_COLLISION = 4;
        public static final int CONSOLE_ID_COLLISION = 5;
        public static final int WRONG_VERSION = 6;
        public static final int WRONG_PASSWORD = 7;
        public static final int COULD_NOT_CONNECT = 8;
        public static final int ROOM_IS_FULL = 9;
        public static final int HOST_BANNED = 10;
        public static final int PERMISSION_DENIED = 11;
        public static final int NO_SUCH_USER = 12;
        public static final int ALREADY_IN_ROOM = 13;
        public static final int CREATE_ROOM_ERROR = 14;
        public static final int HOST_KICKED = 15;
        public static final int UNKNOWN_ERROR = 16;

        public static final int ROOM_UNINITIALIZED = 17;
        public static final int ROOM_IDLE = 18;
        public static final int ROOM_JOINING = 19;
        public static final int ROOM_JOINED = 20;
        public static final int ROOM_MODERATOR = 21;

        public static final int MEMBER_JOIN = 22;
        public static final int MEMBER_LEAVE = 23;
        public static final int MEMBER_KICKED = 24;
        public static final int MEMBER_BANNED = 25;
        public static final int ADDRESS_UNBANNED = 26;

        public static final int CHAT_MESSAGE = 27;
    }

    public interface NetPlayListener {
        void onAddNetPlayMessage(String msg);
    }
}
